package console

import (
	"fmt"
	"io"
	"unicode"
)

const eof rune = -1

type tokenType uint8

const (
	symbol tokenType = iota
	number
	str
	operator
	keyword
	tError
	end
)

var tokenNames = map[tokenType]string{
	symbol: "Symbol",
	number: "Number",
	str:    "String",
	tError: "Scanner error",
	end:    "EOF",
}

func (t tokenType) String() string {
	return tokenNames[t]
}

type stateFunc func(s *scanner) stateFunc

func readNumber(s *scanner) stateFunc {
	switch r := s.pop(); {
	case unicode.IsDigit(r):
		return readNumber
	case r == '.':
		return decimalPart
	case r == 'e':
		return exponent
	case unicode.IsSpace(r) || r == eof:
		s.backup()
		s.emit(number)
		return readToken
	}
	return errorState
}

func decimalPart(s *scanner) stateFunc {
	switch r := s.pop(); {
	case unicode.IsDigit(r):
		return decimalPart
	case unicode.IsSpace(r) || r == eof:
		s.backup()
		s.emit(number)
		return readToken
	case r == 'e':
		return exponent
	}
	return errorState
}

func exponent(s *scanner) stateFunc {
	switch r := s.pop(); {
	case unicode.IsDigit(r):
		return exponent
	case unicode.IsSpace(r) || r == eof:
		s.backup()
		s.emit(number)
		return readToken
	}
	return errorState
}

func readToken(s *scanner) stateFunc {
	switch r := s.pop(); {
	case unicode.IsDigit(r):
		return readNumber
	case unicode.IsLetter(r):
		return readSymbol
	case unicode.IsSpace(r):
		s.ignore()
		return readToken
	case r == '"':
		s.ignore()
		return quotedString
	case r == eof:
		s.emit(end)
		return nil
	}
	return errorState
}

func readSymbol(s *scanner) stateFunc {
	switch r := s.pop(); {
	case unicode.IsLetter(r) || unicode.IsDigit(r) || r == '-' || r == '_':
		return readSymbol
	case unicode.IsSpace(r):
		s.backup()
		s.emit(symbol)
		s.ignore()
		return readToken
	case r == eof:
		s.backup()
		s.emit(symbol)
		s.ignore()
		return readToken
	}
	return errorState
}

func readVariable(s *scanner)

func quotedString(s *scanner) stateFunc {
	switch r := s.pop(); {
	case r == '"':
		s.backup()
		s.emit(str)
		s.ignore()
		return readToken
	case r == eof:
		return errorState
	}
	return quotedString
}

type scanError string

func (p scanError) Error() string {
	return string(p)
}

func errorState(s *scanner) stateFunc {
	s.err = scanError("Parse Error")
	return nil
}

type Command interface {
	Execute(Scope, io.Writer) error
}

type CommandFunc func(Scope, io.Writer) error

type command struct {
	CommandFunc
	args []string
}

func (c *command) Execute(s Scope, out io.Writer) error {
	return c.CommandFunc(s, out)
}

type Scope interface {
	Get(string) interface{}
	Set(string, interface{})
}

type scope struct {
	parent  *scope
	content map[string]interface{}
}

func (this *scope) Get(name string) (value interface{}) {
	for s := this; s != nil && value == nil; s = s.parent {
		value = s.content[name]
	}
	return
}

func (this *scope) Set(name string, value interface{}) {
	this.content[name] = value
}

var commands = map[string]CommandFunc{
	"project": CommandFunc(func(s Scope, out io.Writer) error {
		fmt.Fprintf(out, "scope %v\ncommand project", s)
		return nil
	}),
}
